package se.statifyme.server.services;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;

import se.statifyme.server.ReflectionUtil;
import se.statifyme.server.StatifyMeServerConfig;
import se.statifyme.server.declaration.ServiceDeclaration;
import se.statifyme.server.declaration.ServiceType;
import se.statifyme.shared.RecordServiceConstants;
import se.statifyme.shared.StatifyMeException;

@Path("/" + RecordServiceConstants.REST_PATH + "/recordtype")
public class RecordsService {

//	private static List<ServiceDeclaration> serviceDeclarations;

	@GET
	@Path(RecordServiceConstants.PATH_RECORD + "/")
	@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
	public final Collection<ServiceDeclaration> getAll(@Context HttpServletRequest request) throws ClassNotFoundException, IOException {
		return getServiceDeclarations();
	}
	
	private List<ServiceDeclaration> getServiceDeclarations() {
		return createServiceDeclarations();
//		synchronized (serviceDeclarations) {
//			if (serviceDeclarations == null) {
//				serviceDeclarations = createServiceDeclarations();
//			}
//		}
//		return serviceDeclarations;
	}
	
	private List<ServiceDeclaration> createServiceDeclarations() {
		List<ServiceDeclaration> result;
		try {
			List<Class<?>> classes = ReflectionUtil.getClasses(StatifyMeServerConfig.SERVICES_PACKAGE);
			result = new ArrayList<ServiceDeclaration>();
			for (Class<?> clazz : classes) {
				if (isRecordService(clazz)) {
					result.add(createServiceDeclaration(clazz));
				}
			}
			
		} catch (Exception e) {
			throw new StatifyMeException("Unable to generate service declarations.", e);
		}
		return result;
	}
	
	private ServiceDeclaration createServiceDeclaration(Class<?> clazz) {
		ServiceDeclaration result = new ServiceDeclaration();
		Path path = clazz.getAnnotation(Path.class);
		if (path == null) {
			throw new IllegalStateException("Cannot create a service declaration for service class " + clazz.getName() + " since it's not annotated with Path");
		}
		result.setPath(path.value());
		ServiceType serviceType = clazz.getAnnotation(ServiceType.class);
		if (serviceType == null) {
			result.setDisplayName(clazz.getSimpleName());
		} else {
			result.setDisplayName(serviceType.displayName());
		}
		return result;
	}


	private boolean isRecordService(Class<?> clazz) {
		return clazz.getAnnotation(Path.class) != null && isSubClass(clazz, RecordServiceImpl.class);
	}
	
	private boolean isSubClass(Class<?> subClass, Class<?> superClass) {
		Class<?> parent = subClass.getSuperclass();
		return parent != null && (parent == superClass || isSubClass(parent, superClass));
	}
}
